package com.come4loves.game.server.router;

import io.netty.channel.ChannelHandlerContext;

import java.lang.reflect.Method;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.come4loves.core.comunicate.protocol.DataPacket;
import com.come4loves.core.comunicate.protocol.Response;
import com.come4loves.core.comunicate.protocol.StatusCode;
import com.come4loves.core.comunicate.router.Router;
import com.come4loves.core.comunicate.router.RouterHandler;
import com.come4loves.core.comunicate.router.annotation.Cmd;
import com.come4loves.core.context.SpringContext;
import com.come4loves.core.result.BytesResult;
import com.come4loves.core.result.Result;
import com.come4loves.core.result.ResultFuture;
import com.come4loves.game.server.firewall.Firewall;
import com.come4loves.game.server.session.PlayerContext;

/**
 * 消息路由实现类
 * @author ludd
 *
 */
@Component("GatewayRouterImpl")
public class GatewayRouterImpl extends Router {
	private static final Logger LOGGER = LoggerFactory.getLogger(GatewayRouterImpl.class);
	@Autowired
	private PlayerContext playerContext;
	
	public GatewayRouterImpl() {
	}

	@Override
	public void forward(ChannelHandlerContext context, DataPacket dataPacket) {
		
		
		byte module = dataPacket.getModule();
		byte cmd = dataPacket.getCmd();
		RouterHandler handler = MODULE_MAPS.get(module);
		
		Long actorId = playerContext.getActorId(context);
		Response response = Response.valueOf(dataPacket.getModule(), dataPacket.getCmd(), dataPacket.getArgs());
		if (handler == null) {
			Firewall firewall = (Firewall) SpringContext.getBean(Firewall.class);
			if (firewall.isEnableFirewall() && firewall.blockedByAuthCodeErrors(context, 1)) {
				LOGGER.warn(String.format("In blacklist: [ip: %s]", playerContext.getRemoteIp(context)));
				playerContext.close(context);
				return;
			}
			
			LOGGER.error(String.format("[forward] error module, actorId:[%s], module:[%s]", actorId, module));
			response.setStatusCode(StatusCode.MODULE_NOT_FOUND);
			context.writeAndFlush(response);
			return;
		}
		
		Method method = handler.getMethod(cmd);
		if (method == null) {
			Firewall firewall = (Firewall) SpringContext.getBean(Firewall.class);
			if (firewall.isEnableFirewall() && firewall.blockedByAuthCodeErrors(context, 1)) {
				LOGGER.warn(String.format("In blacklist: [ip: %s]", playerContext.getRemoteIp(context)));
				playerContext.close(context);
				return;
			}
			
			LOGGER.error(String.format("[forward] error module or cmd, actorId:[%s], module:[%s], cmd:[%s]", actorId, module, cmd));
			response.setStatusCode(StatusCode.MODULE_NOT_FOUND);
			context.writeAndFlush(response);
			return;
		}
		
		
		//增加  根据cmd标注判断 帐号是否登陆，角色是否登陆.
		Cmd annotation = handler.getCmd(dataPacket.getCmd());
		if(annotation.checkActorLogin()) {
			if(playerContext.isOnline(context) == false){
				response.setStatusCode(StatusCode.ACTOR_NOT_LOGIN);
				playerContext.push(context, response);
				return;
			}
		}
		
		Runnable r =  invoke(actorId, method, handler, context, dataPacket, response);
		context.executor().execute(r);
		
	}
	
	private Runnable invoke(final long actorId,final Method method, final RouterHandler handler, final ChannelHandlerContext context, final DataPacket dataPacket, final Response response){
		return new Runnable() {
			
			@Override
			public void run() {
				try {
					Object result = method.invoke(handler, context, dataPacket.getValue(), response);
					if (result instanceof Result) {
						Result r = (Result) result;
						response.setStatusCode(r.statusCode);
						handler.sessionWrite(context, response);
					}else if (result instanceof BytesResult) {
						BytesResult r = (BytesResult) result;
						if (r.isFail()) {
							response.setStatusCode(r.statusCode);
							handler.sessionWrite(context, response);
						} else {
							response.setValue(r.bytes);
							handler.sessionWrite(context, response);
						}
					} else if (result instanceof ResultFuture<?>) {
						ResultFuture<?> r = (ResultFuture<?>) result;
						if (r.isFail()) {
							response.setStatusCode(r.statusCode);
							handler.sessionWrite(context, response);
						} else {
							handler.sessionWrite(context, response, r.getListener());
						}
					}
				} catch (Exception exception) {
					LOGGER.error(String.format("[method.invoke] methodName:[%s], actorId:[%s]", method.getName(), actorId), exception);
				}
				
			}
		};
	}
}